/***
 * 
 * @author zhoujunhui
 */
package net.wicp.tams.common.spring.autoconfig.beans;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.FreemarkUtil;
import net.wicp.tams.common.apiext.ReflectAssist;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.constant.StrPattern;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectExceptionRuntime;
import net.wicp.tams.common.spring.autoconfig.IContextInit;
import net.wicp.tams.common.spring.autoconfig.SpringAssit;

@Slf4j
public class AnnotationBean
		implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware, EnvironmentAware {

	private String annotationPackage;

	public String getAnnotationPackage() {
		return annotationPackage;
	}

	public void setAnnotationPackage(String annotationPackage) {
		this.annotationPackage = annotationPackage;
	}

	public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
			throws BeansException {
		// 已支持参数化，都不需要Freemark就会把${}给转化为值。
		if (StringUtil.isNull(this.annotationPackage)) {
			return;
		}
		// freemark 语法更为灵活。支持扫描包的参数化
		this.annotationPackage = FreemarkUtil.getInst().doProcessByTemp(this.annotationPackage,
				this.springAssit.findAllProps());
		if (StringUtil.isNull(this.annotationPackage)) {
			return;
		}
		if (configurableListableBeanFactory instanceof BeanDefinitionRegistry) {
			try {
				// init scanner
				Class<?> scannerClass = ReflectAssist
						.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
				Object scanner = scannerClass
						.getConstructor(new Class<?>[] { BeanDefinitionRegistry.class, boolean.class })
						.newInstance(new Object[] { (BeanDefinitionRegistry) configurableListableBeanFactory, true });
				// add filter
				Class<?> filterClass = ReflectAssist
						.forName("org.springframework.core.type.filter.AnnotationTypeFilter");
				// Object filter2 =
				// filterClass.getConstructor(Class.class).newInstance(MeterTams.class);
				Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter",
						ReflectAssist.forName("org.springframework.core.type.filter.TypeFilter"));
				for (Class<? extends Annotation> annotation : cusTypeMap.keySet()) {
					Object tempAnnotion = filterClass.getConstructor(Class.class).newInstance(annotation);
					addIncludeFilter.invoke(scanner, tempAnnotion);
				}
				// addIncludeFilter.invoke(scanner, filter2);
				// scan packages
				String[] packages = StrPattern.split_pattern.compile().split(annotationPackage);
				Method scan = scannerClass.getMethod("scan", new Class<?>[] { String[].class });
				scan.invoke(scanner, new Object[] { packages });
			} catch (Throwable e) {
				log.error("添加扫描注解失败", e);
			}
		}
	}

	@Override
	public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
		return o;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
		boolean isType = false;
		for (Class<? extends Annotation> key : cusTypeMap.keySet()) {
			String cusFieldStr = cusTypeMap.get(key);
			Annotation declaredAnnotation = o.getClass().getDeclaredAnnotation(key);
			if (declaredAnnotation != null) {
				try {
					TypeBean<Object> serviceBean = (TypeBean) ReflectAssist.forName(cusFieldStr)
							.getConstructor(Annotation.class).newInstance(declaredAnnotation);
					serviceBean.setApplicationContext(SpringAssit.context);
					serviceBean.setRef(o);
					serviceBean.afterPropertiesSet();
				} catch (Exception e) {
					log.error("处理type类型的Annotation失败", e);
				} finally {
					isType = true;
				}
			}
		}
		if (!isType) {
			for (Class<? extends Annotation> key : cusFieldMap.keySet()) {
				String cusFieldStr = cusFieldMap.get(key);
				doWithFields(o, s, key, cusFieldStr);
			}
		}
		return o;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	private void doWithFields(Object o, String s, Class<? extends Annotation> classA, String cusDoWith) {
		List<Field> findFields = SpringAssit.selectFields(o.getClass(), classA);
		if (CollectionUtils.isNotEmpty(findFields)) {
			for (Field field : findFields) {
				try {
					Annotation declaredAnnotation = field.getDeclaredAnnotation(classA);
					FieldBean<Object> serviceBean = (FieldBean) ReflectAssist.forName(cusDoWith)
							.getConstructor(Annotation.class, Field.class).newInstance(declaredAnnotation, field);
					serviceBean.setApplicationContext(SpringAssit.context);
					serviceBean.setRef(o);
					serviceBean.afterPropertiesSet();
				} catch (Exception e) {
					throw new ProjectExceptionRuntime(ExceptAll.Project_default, "设置属性【" + s + "】失败", e);
				}
			}
		}
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringAssit.context = applicationContext;
		// contextInit定义类
		Map<String, String> contextInitMap = Conf.getPre("common.spring.autoconfig.contextInit", true);
		for (String key : contextInitMap.keySet()) {
			try {
				IContextInit tempInit = (IContextInit) Class.forName(contextInitMap.get(key)).getConstructor()
						.newInstance();
				tempInit.init(applicationContext);
			} catch (Exception e) {
				log.error("初始化失败", e);
			}
		}
	}

	private SpringAssit springAssit;

	// 这个首先执行，再是setApplicationContext
	@Override
	public void setEnvironment(Environment environment) {
		this.springAssit = new SpringAssit(environment);
		Properties inputpamas = new Properties();
		Properties tpProps = Conf.copyProperties();
		String[] addSingleAry = StringUtil.isNull(Conf.get("common.spring.autoconfig.addSingle")) ? new String[0]
				: Conf.get("common.spring.autoconfig.addSingle").split(",");
		for (String needAddConf : addSingleAry) {
			if (!tpProps.containsKey(needAddConf)) {
				tpProps.put(needAddConf, "null");
			}
		}
		Map<String, String> allMap = springAssit.findAllProps();
		String[] addPreAry = StringUtil.isNull(Conf.get("common.spring.autoconfig.addPre")) ? new String[0]
				: Conf.get("common.spring.autoconfig.addPre").split(",");
		for (String keystr : allMap.keySet()) {
			boolean needAdd = tpProps.containsKey(keystr);
			if (!needAdd) {
				for (String addPreEle : addPreAry) {
					if (StringUtil.isNotNull(addPreEle) && keystr.startsWith(addPreEle)) {
						needAdd = true;
						break;
					}
				}
			}
			if (needAdd) {
				inputpamas.put(keystr, allMap.get(keystr));
			}
		}
		log.info("input parmas:{}", inputpamas.toString());
		Conf.overProp(inputpamas);

		// 自定义处理类配置
		Map<String, String> pre = Conf.getPre("common.spring.autoconfig.annotation", true);
		for (String key : pre.keySet()) {
			try {
				// String[] keyAry = key.split("\\|");
				int indexOf = key.indexOf(".");
				String elementtype = key.substring(0, indexOf);
				String className = key.substring(indexOf + 1);
				ElementType elementType = ElementType.valueOf(elementtype);
				Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) Class.forName(className);
				switch (elementType) {
				case FIELD:
					cusFieldMap.put(annotationClass, pre.get(key));
					break;
				case TYPE:
					cusTypeMap.put(annotationClass, pre.get(key));
					break;

				default:
					break;
				}
			} catch (Exception e) {
				log.error("error", e);
			}
		}
	}

	private Map<Class<? extends Annotation>, String> cusFieldMap = new HashMap<Class<? extends Annotation>, String>();

	private Map<Class<? extends Annotation>, String> cusTypeMap = new HashMap<Class<? extends Annotation>, String>();

}
